#pragma once
#include<type_traits>
namespace offset_pointer {
    template<typename structT, typename T>
    T& read_from_align_offset(structT* base_ptr, const std::size_t offset)
    {
        return *(reinterpret_cast<T*>(reinterpret_cast<unsigned char*>(base_ptr) + offset));
    }

    //to fix the std::conditional_t with SFINAE
    template<bool con, class T> struct invoke_type_if_impl
    {
        using type = T;
    };

    template<class T> struct invoke_type_if_impl<true, T>
    {
        using type = typename T::type;
    };

    template<bool con, class T> using invoke_type_if = typename invoke_type_if_impl<con, T>::type;

    //stimulate members layout in a structure
    //a pointer can advance with given alignment size and certain size at compile time
    template<std::size_t alignment_size, std::size_t layer_offset, std::size_t offset> struct align_offset_ptr
    {
        static constexpr std::size_t layer = layer_offset;//alignment layer as base
        static constexpr std::size_t value = offset;//offset from the current layer
        template<std::size_t lay> struct advance_layer
        {
            using type = align_offset_ptr<alignment_size, layer + lay, 0>;
        };
        template<bool can_be_placed, std::size_t seek_offset> struct seek_impl
        {
            using type = align_offset_ptr<alignment_size, layer, value>;
        };
        template<std::size_t seek_offset> struct seek_impl<false, seek_offset>
        {
            using type = typename std::conditional_t<
                (value + seek_offset > alignment_size),
                typename advance_layer<1>::type,
                align_offset_ptr<alignment_size, layer_offset, value + 1>
                >::template seek<seek_offset>;
        };
        template<std::size_t seek_offset> using seek = typename seek_impl<value% seek_offset == 0, seek_offset>::type;

        template<std::size_t advance_offset> struct advance_impl
        {
            using type = std::conditional_t<(value + advance_offset < alignment_size),
                align_offset_ptr<alignment_size, layer, value + advance_offset>,
                std::conditional_t<
                static_cast<bool>(advance_offset% alignment_size),
                typename advance_layer<advance_offset / alignment_size + 1>::type,
                typename advance_layer<advance_offset / alignment_size>::type>
            >;
        };
        template<std::size_t advance_offset> using advance = typename advance_impl<advance_offset>::type;
       
        static constexpr std::size_t offset_v = layer * alignment_size + value;
    };

    //align offset iterator impl
    template<class align_offset_pointer, class T>
    struct align_offset_advance_basic
    {
        using type = typename align_offset_pointer
            ::template seek<sizeof(T)>
            ::template advance<sizeof(T)>;
    };

    template<class align_offset_pointer, class T>
    using align_offset_advance_basic_t =
        typename align_offset_advance_basic<align_offset_pointer, T>::type;

    //array in structure is considered as N members
    template<class align_offset_pointer, class array_t> struct align_offset_advance_array_impl {};
    template<class align_offset_pointer, class basic_type, std::size_t N>
    struct align_offset_advance_array_impl<align_offset_pointer, basic_type[N]>
    {
        using type = typename align_offset_advance_array_impl<
            align_offset_advance_basic_t<align_offset_pointer, basic_type>,
            basic_type[N - 1]>::type;
    };
    template<class align_offset_pointer, class basic_type>
    struct align_offset_advance_array_impl<align_offset_pointer, basic_type[1]>
    {
        using type = align_offset_advance_basic_t<align_offset_pointer, basic_type>;
    };

    template<class align_offset_pointer, class array_t>
    using align_offset_advance_array_t = typename align_offset_advance_array_impl<
        align_offset_pointer, array_t
    >::type;

    template<class align_offset_pointer, class T> 
    using align_offset_advance_t = std::conditional_t<
        std::is_array_v<T>,
        invoke_type_if<std::is_array_v<T>, align_offset_advance_array_impl<align_offset_pointer, T>>,
        invoke_type_if<!std::is_array_v<T>, align_offset_advance_basic<align_offset_pointer, T>>
    >;

    
    template<class align_offset_pointer, class T> struct align_offset_seek_impl
    {
        using type = std::conditional_t<std::is_array_v<T>,
            typename align_offset_pointer::template seek<sizeof(std::remove_extent_t<T>)>,
            typename align_offset_pointer::template seek<sizeof(T)>
        >;
    };

    template<class align_offset_pointer, class T>
    using align_offset_seek_t = typename align_offset_seek_impl<align_offset_pointer, T>::type;

    template<class align_offset_pointer, class offset_map, std::size_t N>
    struct align_offset_iterator
    {
        using type = align_offset_pointer;
    };

    template<class align_offset_pointer, std::size_t N,
        template<class ...> class offset_map, class first, class ...rest>
    struct align_offset_iterator<align_offset_pointer, offset_map<first, rest...>, N>
    {
        using recursive_type = align_offset_iterator<
            align_offset_advance_t<align_offset_pointer, first>,
            offset_map<rest...>, N - 1>;
        using type = typename recursive_type::type;
        using get = typename recursive_type::get;
        using value_type = typename recursive_type::value_type;
    };
    template<class align_offset_pointer, template<class...> class offset_map, class first, class ...rest>
    struct align_offset_iterator<align_offset_pointer, offset_map<first, rest...>, 0>
    {
        //kept pointer
        using type = align_offset_pointer;
        //common interface
        using get = align_offset_seek_t<type, first>;
        //common interface for Ty info
        using value_type = first;
    };

    template<class ...> struct offset_pattern {};
    template<std::size_t alignment_size, class ...typs> 
    struct offset_vector
    {
        template<std::size_t I>
        using iterator = align_offset_iterator<
            align_offset_ptr<alignment_size, 0, 0>,
            offset_pattern<typs...>, I>;

        template<std::size_t I> 
        static constexpr std::size_t offset_v = iterator<I>::get::offset_v;

        template<std::size_t I> 
        using value_type = typename iterator<I>::value_type;

        //get specified indices by any other struct like std::index_sequence or meta_traits::meta_array
        template<template<std::size_t ...> class indices_container, std::size_t...I>
        static constexpr auto offset_array(indices_container<I...>) {
            return indices_container<offset_v<I>...>{};
        }
    };
    //dynamically
    template<typename structT, typename T>
    std::size_t align_offset_seek(std::size_t& layer, std::size_t& offset)
    {
        constexpr std::size_t alignment_size = alignof(structT);
        constexpr std::size_t offset_size = sizeof(T);
        if (offset % offset_size) {
            offset += 1;
            if (offset + offset_size > alignment_size) {
                layer += 1;
                offset = 0;
            }
            return align_offset_seek<structT, T>(layer, offset);
        }

        return alignment_size * layer + offset;
    }

    template<typename structT, typename T>
    void align_offset_advance(std::size_t& layer, std::size_t& offset)
    {
        constexpr std::size_t alignment_size = alignof(structT);
        constexpr std::size_t advance_size = sizeof(T);
        if (offset + advance_size < alignment_size) {
            offset += advance_size;
        }
        else {
            layer += advance_size / alignment_size;
            layer += advance_size % alignment_size ? 1 : 0;
            offset = 0;
        }
    }

    template<class F, class TT> struct init_and_call
    {
        F f;
        TT* x;
        inline static std::size_t value = 0;
        inline static std::size_t layer = 0;

        template<class T>
        operator T() {
            const std::size_t off = align_offset_seek<TT, T>(layer, value);
            f(read_from_align_offset<TT, T>(x, off));
            align_offset_advance<TT, T>(layer, value);
            return T{};
        };
    };

    template<class F, class T, class first, class ...rest> constexpr void for_each_member_impl(F f, T* x)
    {
        if constexpr (!requires{ T{ std::declval<first>(), std::declval<rest>()...}; }) {
            
            T{ rest{f, x}... };

        }
        else {
            for_each_member_impl<F, T, init_and_call<F, T>, first, rest...>(f, x);

        }
        init_and_call<F, T>::value = 0;
        init_and_call<F, T>::layer = 0;
    }

    template<class F, class T> constexpr void for_each_member(F f, T& x)
    {
        for_each_member_impl<F, T, init_and_call<F, T>>(f, &x);
    }

#include"meta_stream.hpp"
    using namespace meta_ios;
    using namespace exp_utilities;
    namespace calc_detail {
        struct make_align
        {
            template<class this_align, class T> using apply = std::conditional_t<
                (alignof(T) > this_align::value), Idx<alignof(T)>, this_align>;
        };

        using align_calc_ostream = meta_object<Idx<0>, make_align>;

        template<class TL> using decl_align_t = meta_all_transfer<
            align_calc_ostream,
            meta_istream<TL>
        >::to::type;

        template<class align_ptr, class self_type>
        struct align_ptr_status {
            //when invoked, advance the pointer to the next offset
            template<class T>
            using apply = std::conditional_t<std::is_same_v<self_type, exp_utilities::literal_types::no_exist_type>,
                align_ptr_status<align_ptr, T>,
                align_ptr_status<align_offset_advance_t<align_ptr, self_type>, T>
            >;

            //seek current data offset
            using ptr = align_offset_seek_t<align_ptr, self_type>;

            //save type info
            using type = self_type;
        };

        struct offset_update_f {
            template<class this_align_ptr_status, class T>
            using apply = meta_invoke<this_align_ptr_status, T>;
        };

    }

    template<std::size_t alignment_size>
    using align_offset_calc_ostream = meta_object<
        calc_detail::align_ptr_status<offset_pointer::align_offset_ptr<alignment_size, 0, 0>, exp_utilities::literal_types::no_exist_type>,
        calc_detail::offset_update_f
    >;

    template<class ...Tys>
    constexpr std::size_t decl_align = calc_detail::decl_align_t<exp_list<Tys...>>::value;

    template<class ...Tys>
    constexpr std::size_t field_size = meta_fold<
        meta_all_transfer<
            align_offset_calc_ostream<decl_align<Tys...>>,
            meta_istream_list<Tys...>
        >, protocols::stream_to_t>::ptr
            ::template advance<sizeof(typename exp_list<Tys...>::back)>
                ::template seek<decl_align<Tys...>>
                    ::offset_v;
}